/**
 *
 */
package es.silenus.detecta.dao.impl;

import es.silenus.detecta.dao.ImportedFileDAO;

import es.silenus.detecta.model.ImportedFile;
import es.silenus.detecta.model.ImportedFileStatus;

import org.hibernate.FetchMode;

import org.hibernate.criterion.DetachedCriteria;
import org.hibernate.criterion.MatchMode;
import org.hibernate.criterion.Restrictions;

import org.springframework.jdbc.core.SqlParameter;
import org.springframework.jdbc.core.namedparam.MapSqlParameterSource;
import org.springframework.jdbc.core.simple.SimpleJdbcCall;

import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import org.apache.commons.logging.LogFactory;
import org.apache.commons.logging.Log;

import java.util.List;
import java.util.Map;


/**
 * The Class ImportedFileDAOImpl.
 */
public class ImportedFileDAOImpl extends ReadWriteDAOImpl<ImportedFile, Long> implements ImportedFileDAO {

	/**
	 * Logger for this class.
	 */
	private static final Log LOG = LogFactory.getLog(ImportedFileDAOImpl.class);

	/**
	 * Load entities.
	 */
	private List<String> loadEntities;

	/**
	 * Sets the load entity names.
	 *
	 * @param loadEntities load entity names.
	 */
	public void setLoadEntities(final List<String> loadEntities) {
		this.loadEntities = loadEntities;
	}

	/**
	 * Override this method to provide a find by example page criteria.
	 *
	 * @param criteria the criteria.
	 * @param object the sample object.
	 *
	 * @return the criteria.
	 */
	protected DetachedCriteria preparePageCriteria(final DetachedCriteria criteria, final Map<String, Object> object) {
		criteria.setFetchMode("organization", FetchMode.JOIN);

		Object temp = object.get("organizationId");

		if(temp != null) {
			criteria.add(Restrictions.eq("organization.id", temp));
		}

		temp = object.get("filename");

		if(temp != null) {
			criteria.add(Restrictions.ilike("filename", (String)temp, MatchMode.ANYWHERE));
		}

		temp = object.get("status");

		if(temp != null) {
			criteria.add(Restrictions.eq("status", ImportedFileStatus.getById((Integer)temp)));
		}

		temp = object.get("fromImportDate");

		if(temp != null) {
			criteria.add(Restrictions.ge("importDate", temp));
		}

		temp = object.get("toImportDate");

		if(temp != null) {
			criteria.add(Restrictions.le("importDate", temp));
		}

		return criteria;
	}

	/**
	 * Override this method to provide a find by example list criteria.
	 *
	 * @param criteria the criteria.
	 * @param object the sample object.
	 *
	 * @return the criteria.
	 */
	protected DetachedCriteria prepareListCriteria(final DetachedCriteria criteria, final Map<String, Object> object) {
		return preparePageCriteria(criteria, object);
	}


	private boolean canProcessFile(final ImportedFile file) {
		boolean result = false;

		ImportedFileStatus status = null;

		if(file != null) {
			status = file.getStatus();

			switch (status) {
				case VALIDATED:
					result = true;
					break;
				default:
					break;
			}
		}

		if(LOG.isDebugEnabled()) {
			LOG.debug(String.format("Can process file [%s - %s]? %b ", file, status, result));
		}

		return result;
	}

	/**
	 * Process file contents.
	 *
	 * @param id the file id.
	 */
	@Transactional(readOnly = true, propagation = Propagation.SUPPORTS)
	public void process(long id) {
		ImportedFile file = findById(id);

		if(canProcessFile(file)) {
			final String procedureName = "import_load";
			final String procedureParameterName = "imported_file";

			// Call store procedure
			SimpleJdbcCall procedure = new SimpleJdbcCall(dataSource);

			procedure.withProcedureName(procedureName).withoutProcedureColumnMetaDataAccess();

			procedure.declareParameters(new SqlParameter(procedureParameterName, java.sql.Types.BIGINT));

			MapSqlParameterSource parameters = new MapSqlParameterSource();

			parameters.addValue(procedureParameterName, id);
			procedure.execute(parameters);

			// Evict entity
			getHibernateTemplate().evict(file);

			// Clear caches
			clearEntityCaches(loadEntities);
		}
	}


	private boolean canClearFile(final ImportedFile file) {
		boolean result = false;

		ImportedFileStatus status = null;

		if(file != null) {
			status = file.getStatus();

			switch (status) {
				case VALIDATED:
				case UPLOADED:
					result = true;
					break;
				default:
					result = false;
					break;
			}
		}

		if(LOG.isDebugEnabled()) {
			LOG.debug(String.format("Can clear file [%s - %s]? %b ", file, status, result));
		}

		return result;
	}

	/**
	 * Clear file contents.
	 *
	 * @param id the file id.
	 */
	@Transactional(readOnly = true, propagation = Propagation.SUPPORTS)
	public void clear(long id) {
		ImportedFile file = findById(id);

		if(canClearFile(file)) {
			final String procedureName = "clear_load";
			final String procedureParameterName = "imported_file";

			// Call store procedure
			SimpleJdbcCall procedure = new SimpleJdbcCall(dataSource);

			procedure.withProcedureName(procedureName).withoutProcedureColumnMetaDataAccess();

			procedure.declareParameters(new SqlParameter(procedureParameterName, java.sql.Types.BIGINT));

			MapSqlParameterSource parameters = new MapSqlParameterSource();

			parameters.addValue(procedureParameterName, id);
			procedure.execute(parameters);

			// Evict entity
			getHibernateTemplate().evict(file);

			// Clear caches
			clearEntityCaches(loadEntities);
		}
	}


	private boolean canValidateFile(final ImportedFile file) {
		boolean result = false;

		ImportedFileStatus status = null;

		if(file != null) {
			status = file.getStatus();

			switch(status) {
				case UPLOADED:
				case VALIDATED:
					result = true;
					break;
				default:
					result = false;
					break;
			}

		}

		if(LOG.isDebugEnabled()) {
			LOG.debug(String.format("Can validate file [%s - %s]? %b ", file, status, result));
		}

		return result;
	}

	/**
	 * Validate already imported file.
	 *
	 * @param id the file.
	 */
	@Transactional(readOnly = true, propagation = Propagation.SUPPORTS)
	public void validate(long id) {
		ImportedFile file = findById(id);

		if(canValidateFile(file)) {
			final String procedureName = "validate_load";
			final String procedureParameterName = "imported_file";

			// Call store procedure
			SimpleJdbcCall procedure = new SimpleJdbcCall(dataSource);

			procedure.withProcedureName(procedureName).withoutProcedureColumnMetaDataAccess();

			procedure.declareParameters(new SqlParameter(procedureParameterName, java.sql.Types.BIGINT));

			MapSqlParameterSource parameters = new MapSqlParameterSource();

			parameters.addValue(procedureParameterName, file.getId());
			procedure.execute(parameters);

			// Evict entity
			getHibernateTemplate().evict(file);

			// Clear caches
			clearEntityCaches(loadEntities);
		}
	}
}
